package Simulator;


import java.awt.Container;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import Simulator.CarThread;

public class Simulator extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int numCars;
	private int lengthRoad;
	private float slowDownProbability;
	private float incomingCarProbability;
	private PrintWriter outFile;
	private Random randomGenerator;
	private Road road;
	private JPanel canvas;



	public Simulator(int numCars, int maxSpeed, int lengthRoad,
			float slowDownProbability, float incomingCarProbability) {
		this.numCars = numCars;
		this.lengthRoad = lengthRoad;
		this.slowDownProbability = slowDownProbability;
		this.incomingCarProbability = incomingCarProbability;
		// road = new CircularRoad(lengthRoad,maxSpeed);
		road = new StraightRoad(lengthRoad, maxSpeed);
		setCars();

		setSize(1000, 200);
		setLocationRelativeTo(rootPane);
		setTitle("Traffic Visual");
		setVisible(true);

		Container contentPane = getContentPane();
		canvas = new JPanel();
		contentPane.add(canvas, "Center");
		JPanel p = new JPanel();

		//contentPane.add(p, "South");
	}

	public void simulate(int iterations){
		for (int i = 0; i < iterations; i++) {
			// Add a new car 
			addCar();
			Ball b = new Ball(canvas);
			b.start();
			// first write the iteration number to the file
			outFile.print("\n"+i);
			// on each iteration what we have to do is scan the road, and for
			// each car found do the first 3 steps
			for (int j = lengthRoad - 1; j >= 0; j--){
				Car car = road.getCar(j);
				if (car != null){
					car.accelerate();
					car.decelerate();
					car.randomizeSpeed(slowDownProbability);	
				}
			}
			// write "-" for the cars that have left the road
			int numCarsLeft = numCars - road.getNumberCarsOnRoad();
			for (int j = 0; j < numCarsLeft; j++){
				outFile.print("\t-");
			}
			// then do the 4th step, move all the cars and write their new positions
			for (int j = lengthRoad -1; j >= 0; j--){
				Car car = road.getCar(j);
				if (car != null){
					car.moveCar();
					// write the result to the file
					outFile.print("\t"+car.getCellIndex());
				}
			}
		}
	}

	/**
	 * Sets the PrintWriter outFile to the selected file, and also returns it
	 * 
	 * @param fileName
	 *            The name of the file you want to create
	 * @return the PrintWriter to the file
	 */
	public PrintWriter printToFile(String fileName) {
		PrintWriter outFile = null;
		try {
			outFile = new PrintWriter(new BufferedWriter(new FileWriter(
					fileName)));
		} catch (IOException e) {
			System.err
			.println("The file " + fileName + " could not be created");
		}
		// Set the PrintWriter of the simulator to the file
		this.outFile = outFile;
		return outFile;
	}

	/**
	 * Randomly places cars in the road cells
	 * 
	 */
	public void setCars() {
		randomGenerator = new Random();
		for (int i = 0; i < numCars; i++) {
			int place = randomGenerator.nextInt(lengthRoad-1);
			// Check if the place is already occupied, if is try with next place
			while (road.getCar(place) != null) {
				place++;
			}
			// Create the car
			Car car = new Car(road, place);
		}
	}

	/**
	 * Add one car on first cell, randomly
	 */
	public void addCar() {
		randomGenerator = new Random();
		int place = 0;
		// Check if the place is already empty
		if (road.getCar(place) == null) {
			if(randomGenerator.nextFloat() < incomingCarProbability){
				// Create the car, and increase the number of cars
				Car car = new Car(road, place);     
				numCars++;
			}
		}
	}

	/**
	 * Main class Usage: Simulator numberCars maxSpeed lengthRoad probability
	 * iterations OutputFile
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		if (args.length != 7) {
			System.out
			.println("Usage: Simulator numberCars maxSpeed lengthRoad slowDownProbability incomingCarProbability numIterations outputFile");
		} else {
			Simulator simulator = new Simulator(Integer.parseInt(args[0]),
					Integer.parseInt(args[1]), Integer.parseInt(args[2]), Float
					.parseFloat(args[3]), Float.parseFloat(args[4]));

			// set the output file
			PrintWriter outFile = simulator.printToFile(args[6]);
			Date date = new Date();
			outFile.println("#Simulation of traffic "
					+ date.toString());
			outFile.println("#number of cars: "+simulator.numCars+" maxSpeed: "+simulator.road.getMaxSpeed()
					+" length of the road:"+simulator.lengthRoad+" slowDownProbability:"+simulator.slowDownProbability+
					" incomingCarProbability:"+Float.parseFloat(args[4])+" number of iterations:"+args[5]);

			// simulate
			simulator.simulate(Integer.parseInt(args[5]));

			// close output file
			outFile.close();

			simulator.generateGnuplotScript(args[6] + ".script", args[6]);

			System.out.println("DONE, check output file "+args[6]);
		}
	}

	/**
	 * Generates a gnuplot script
	 * 
	 * @param fileName
	 *            the gnuplot script filename
	 * @param dataFileName
	 *            the data filename
	 */
	public void generateGnuplotScript(String fileName, String dataFileName) {
		PrintWriter scriptFile;
		try {
			scriptFile = new PrintWriter(new BufferedWriter(new FileWriter(
					fileName)));
			scriptFile.println("#Plotting script for " + dataFileName
					+ " with " + numCars + " cars");
			scriptFile.println("set ylabel \"Time\"");
			scriptFile.println("set xlabel \"Cells\"");
			//scriptFile.println("set terminal postscript \nset output \""+dataFileName+".ps\"");
			// plot a line delimiting the end of road
			//scriptFile.print("plot " + this.lengthRoad + " notitle");
			scriptFile.print("plot 0 notitle");
			// create plotting instructions for each car
			for (int i = 0; i < numCars; i++) {
				int column = i + 2; // i starts at 0, so 0+1 is the time column,
				// and 0+2 is the column for first car
				scriptFile.print(", \\\n '" + dataFileName + "' u " + column + ":1 notitle w lines lt -1");
			}
			scriptFile.println("\npause -1 \"Press enter to continue, check "+dataFileName+".ps\"");
			scriptFile.close();
		} catch (IOException e) {
			System.err.println("Unable to create script file");
		}
	}

}
